package runner;
import java.util.Random;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.awt.Color;

import controller.Field;
import controller.Randomizer;

import view.AnimatedView;
import view.BarChart;
import view.CirkelView;
import view.SimulatorView;
import view.TextView;
import view.TotalView;
import vossenenkonijnen.Actor;
import vossenenkonijnen.Fox;
import vossenenkonijnen.Grass;
import vossenenkonijnen.Hunter;
import vossenenkonijnen.Location;
import vossenenkonijnen.Rabbit;
import vossenenkonijnen.Wolf;

/**
 * A simple predator-prey simulator, based on a rectangular field
 * containing rabbits, foxes, wolves and hunters.
 * 
 * @author David J. Barnes and Michael Kolling (Expansion: Projectgroep 5)
 * @version 2008.03.30
 */
public class Simulator implements Runnable
{
    // Constants representing configuration information for the simulation.
    // The default width for the grid.
    private static final int DEFAULT_WIDTH = 50;
    // The default depth of the grid.
    private static final int DEFAULT_DEPTH = 50;
    // The probability that a fox will be created in any given grid position.
    private static final double FOX_CREATION_PROBABILITY = 0.02;
    // The probability that an actor will be created in any given grid position.
    private static final double RABBIT_CREATION_PROBABILITY = 0.07; 
    private static final double WOLF_CREATION_PROBABILITY = 0.007;
    private static final double HUNTER_CREATION_PROBABILITY = 0.002;
    private static final double GRASS_CREATION_PROBABILITY = 0.7;
    
    public boolean onlySteps = false;
    public int stepIncrease = 0;
    //instance variable to run the Thread
    public boolean run = false;

    // List of animals in the field.
    // private List<Animal> animals;
    private List<Actor> actors;
    // The current state of the field.
    private Field field;
    // The current step of the simulation.
    private int step;
    // The views of the simulation.
    private AnimatedView view;
    private CirkelView pieView;
    private BarChart barView;
    private TextView textView;
    
    // List that holds the views
    private List<SimulatorView> views;
    
    /**
     * Create a simulation with self determined parameters
     * @param depth height of the simulation field
     * @param width width of the simulation field
     * @param levenKonijnen maximum age a rabbit can reach
     * @param levenVossen maximum age a fox can reach
     * @param levenWolven maximum age a wolf can reach
     * @param nakomelingenKonijnen maximum of births a rabbit can give
     * @param nakomelingenVossen maximum of births a fox can give
     * @param nakomelingenWolven maximum of births a wolf can give
     * @param voortplantingKonijnen minimal age where a rabbit can give birth
     * @param voortplantingVossen minimal age where a fox can give birth
     * @param voortplantingWolven minimal age where a wolf can give birth
     */
    public Simulator(int depth, int width, int levenKonijnen, int levenVossen, int levenWolven, int nakomelingenKonijnen,
			int nakomelingenVossen, int nakomelingenWolven, int voortplantingKonijnen, int voortplantingVossen,
			int voortplantingWolven) {
    	
    	// Set the parameters in the animal classes
    	Rabbit.setMaxAge(levenKonijnen);
    	Rabbit.setBreedingAge(voortplantingKonijnen);
    	Rabbit.setMaxLittersize(nakomelingenKonijnen);
    	
    	Fox.setMaxAge(levenVossen);
    	Fox.setBreedingAge(voortplantingVossen);
    	Fox.setMaxLittersize(nakomelingenVossen);
    	
    	Wolf.setMaxAge(levenWolven);
    	Wolf.setBreedingAge(voortplantingWolven);
    	Wolf.setMaxLittersize(nakomelingenWolven);
    	
    	// Set default sizes if invalid width or depth
    	if(width <= 0 || depth <= 0) {
            System.out.println("The dimensions must be greater than zero.");
            System.out.println("Using default values.");
            depth = DEFAULT_DEPTH;
            width = DEFAULT_WIDTH;
        }
        
        actors = new ArrayList<Actor>();
        field = new Field(depth, width);
                
        // Create a view of the state of each location in the field.
        view = new AnimatedView(this);
        // Create a piechart view
        pieView = new CirkelView();
        // Create a barchart view
        barView = new BarChart();
        // Create a textview
        textView = new TextView();
        
        // Add the views to arraylist
        views = new ArrayList<SimulatorView>();
        views.add(view);
        views.add(pieView);
        views.add(barView);
        views.add(textView);
        
        // Set the colors of the actors
        setActorColors();
                
        onlySteps = false;
        
        // Create the frame with simulation views
        new TotalView(this, view, pieView, barView, textView);
        
        reset();

	}
    
    /**
     * Set the colors for all of the actors
     */
    public void setActorColors() {
    	for(SimulatorView v: views) {
        	v.setColor(Rabbit.class, Color.orange);
            v.setColor(Fox.class, Color.blue);
            v.setColor(Wolf.class, Color.green);
            v.setColor(Hunter.class, Color.red);
        }
    }

	/**
     * Run the simulation from its current state for a reasonably long period,
     * e.g. 500 steps.
     */
    public void runLongSimulation()
    {
        simulate(500);
    }
    
    /**
     * Run the simulation from its current state for the given number of steps.
     * Stop before the given number of steps if it ceases to be viable.
     * @param numSteps The number of steps to run for.
     */
    public void simulate(int numSteps)
    {
        stepIncrease = numSteps;
        for(int step = 1; step <= numSteps && view.isViable(field); step++) {
           simulateOneStep();
           
        }
    }
    
    /**
     * Run the simulation from its current state for a single step.
     * Iterate over the whole field updating the state of each
     * fox and rabbit.
     */
    public void simulateOneStep()
    {
        step++;
        // Provide space for newborn animals.
        List<Actor> newActors = new ArrayList<Actor>();        
        // Let all actors act.
        for(Iterator<Actor> it = actors.iterator(); it.hasNext(); ) {
            Actor actor = it.next();
            actor.act(newActors);
            if(! actor.isActive()) {
                it.remove();
            }
        }
               
        // Add the newly born foxes and rabbits to the main lists.
        actors.addAll(newActors);
        
        // Update all the views
        for(SimulatorView v: views) {
        	v.showStatus(step, field);
        }
    }
        
    /**
     * Reset the simulation to a starting position.
     */
    public void reset()
    {
        step = 0;
        actors.clear();
        populate();
        
        // Show the starting state in the view.
        for(SimulatorView v: views) {
        	v.showStatus(step, field);
        }
    }
    
    /**
     * Randomly populate the field with foxes and rabbits.
     */
    private void populate()
    {
        Random rand = Randomizer.getRandom();
        field.clear();
        for(int row = 0; row < field.getDepth(); row++) {
            for(int col = 0; col < field.getWidth(); col++) {
                if(rand.nextDouble() <= WOLF_CREATION_PROBABILITY) {
                    Location location = new Location(row, col);
                    Wolf wolf = new Wolf(true, field, location);
                    actors.add(wolf);
                }
                else if(rand.nextDouble() <= FOX_CREATION_PROBABILITY) {
                    Location location = new Location(row, col);
                    Fox fox = new Fox(true, field, location);
                    actors.add(fox);
                }
                else if(rand.nextDouble() <= RABBIT_CREATION_PROBABILITY) {
                    Location location = new Location(row, col);
                    Rabbit rabbit = new Rabbit(true, field, location);
                    actors.add(rabbit);
                }
                else if(rand.nextDouble() <= HUNTER_CREATION_PROBABILITY) {
                    Location location = new Location(row, col);
                    Hunter hunter = new Hunter(field, location);
                    actors.add(hunter);
                }
                if(rand.nextDouble() <= GRASS_CREATION_PROBABILITY) {
                	Location location = new Location(row, col);
                	Grass grass = new Grass(field, location);
                	actors.add(grass);
                }
                // else leave the location empty.
            }
        }
    }
    
    /**
     * Get the simulation field
     * @return field the simulation field
     */
    public Field getField()
    {
    	return field;
    }

    /**
     * Start simulation
     */
    public void start(){
    	new Thread(this).start();
    }
    
    /**
     * Hold simulation
     */
    public void stop(){
    	run = false;
    }
    /**
     * Run method to display all steps, makes an animation
     */
	@Override
	public void run() {
		run=true;
			
		int stepCounter = 0;
		int speed = 100;
		while(run && view.isViable(field)) {
			simulateOneStep();
			try {
				Thread.sleep(speed);
			} catch (Exception e){}
			stepCounter ++;
			if(stepCounter >= stepIncrease &&  onlySteps){
				run = false;
			}
			  
		}
		onlySteps = false;
	}
	
			
}

